package junior.db.dao.mybatis.type;

import junior.db.dao.jdbc.JdbcHelper;
import junior.db.dao.mybatis.interceptor.MybatisConfigurationHolder;
import junior.util.function.CheckedFunction;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @author wubo
 * @date 2024/9/25 11:32
 */
public class PersistEnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {
    private final Class<E> type;
    private final Class<?> valueType;
    private final CheckedFunction<Object, Object> toFunc;
    
    public PersistEnumTypeHandler(Class<E> type) {
        if (type == null) {
            throw new IllegalArgumentException("Type argument cannot be null");
        }
        this.type = type;
        Pair<Class<?>, CheckedFunction<Object, Object>> info = JdbcHelper.getPersistToInfo(type);
        if (info != null) {
            valueType = info.getLeft();
            toFunc = info.getRight();
        } else {
            valueType = null;
            toFunc = null;
        }
    }
    
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
        if (valueType != null) {
            Configuration configuration = MybatisConfigurationHolder.getConfiguration();
            if (configuration != null) {
                TypeHandler handler = configuration.getTypeHandlerRegistry().getTypeHandler(valueType);
                Object value = toFunc.apply(parameter);
                handler.setParameter(ps, i, value, jdbcType);
                return;
            }
        }
        if (jdbcType == null) {
            ps.setString(i, parameter.name());
        } else {
            ps.setObject(i, parameter.name(), jdbcType.TYPE_CODE); // see r3589
        }
    }
    
    @Override
    public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
        if (valueType != null) {
            return (E) JdbcHelper.getValue(rs, columnName, type);
        }
        String s = rs.getString(columnName);
        return s == null ? null : Enum.valueOf(type, s);
    }
    
    @Override
    
    public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        if (valueType != null) {
            return (E) JdbcHelper.getValue(rs, columnIndex, type);
        }
        String s = rs.getString(columnIndex);
        return s == null ? null : Enum.valueOf(type, s);
    }
    
    @Override
    public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        String s = cs.getString(columnIndex);
        return s == null ? null : Enum.valueOf(type, s);
    }
}
